"""Conversation and Message data models for MongoDB."""

from typing import List, Optional, Dict, Any
from datetime import datetime
from dataclasses import dataclass, field, asdict
from enum import Enum
import uuid
from bson import ObjectId


class ConversationStatus(Enum):
    """Conversation status enumeration."""
    ACTIVE = "active"
    ARCHIVED = "archived"
    DELETED = "deleted"


class MessageRole(Enum):
    """Message role enumeration."""
    USER = "user"
    ASSISTANT = "assistant"
    SYSTEM = "system"


class ConversationCategory(Enum):
    """Conversation category enumeration."""
    CREATIVE = "创作"
    TECHNICAL = "技术"
    OTHER = "其他"


@dataclass
class MessageMetadata:
    """Message metadata structure."""
    edited: bool = False
    edit_history: List[Dict[str, Any]] = field(default_factory=list)
    attachments: List[Dict[str, Any]] = field(default_factory=list)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for MongoDB storage."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'MessageMetadata':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class Message:
    """Message subdocument structure."""
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    role: MessageRole = MessageRole.USER
    content: str = ""
    timestamp: datetime = field(default_factory=datetime.utcnow)
    model: Optional[str] = None
    tokens_used: int = 0
    cost: float = 0.0
    metadata: MessageMetadata = field(default_factory=MessageMetadata)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for MongoDB storage."""
        return {
            "id": self.id,
            "role": self.role.value,
            "content": self.content,
            "timestamp": self.timestamp,
            "model": self.model,
            "tokens_used": self.tokens_used,
            "cost": self.cost,
            "metadata": self.metadata.to_dict()
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'Message':
        """Create from dictionary."""
        data = data.copy()
        data['role'] = MessageRole(data['role'])
        data['metadata'] = MessageMetadata.from_dict(data.get('metadata', {}))
        return cls(**data)


@dataclass
class ConversationSettings:
    """Conversation settings structure."""
    temperature: float = 0.7
    max_tokens: int = 2000
    top_p: float = 0.9
    frequency_penalty: float = 0.0
    presence_penalty: float = 0.0
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'ConversationSettings':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class ConversationStatistics:
    """Conversation statistics structure."""
    total_messages: int = 0
    total_tokens: int = 0
    total_cost: float = 0.0
    last_message_at: Optional[datetime] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "total_messages": self.total_messages,
            "total_tokens": self.total_tokens,
            "total_cost": self.total_cost,
            "last_message_at": self.last_message_at
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'ConversationStatistics':
        """Create from dictionary."""
        return cls(**data)
    
    def update_with_message(self, message: Message):
        """Update statistics with a new message."""
        self.total_messages += 1
        self.total_tokens += message.tokens_used
        self.total_cost += message.cost
        self.last_message_at = message.timestamp


@dataclass
class SharingInfo:
    """Sharing information structure."""
    is_shared: bool = False
    share_id: Optional[str] = None
    share_expires_at: Optional[datetime] = None
    share_views: int = 0
    share_password: Optional[str] = None  # Optional password protection
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "is_shared": self.is_shared,
            "share_id": self.share_id,
            "share_expires_at": self.share_expires_at,
            "share_views": self.share_views,
            "share_password": self.share_password
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'SharingInfo':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class Conversation:
    """Main Conversation document structure for MongoDB."""
    _id: Optional[ObjectId] = None
    user_id: str = ""
    title: str = "New Conversation"
    status: ConversationStatus = ConversationStatus.ACTIVE
    tags: List[str] = field(default_factory=list)
    category: ConversationCategory = ConversationCategory.OTHER
    model_preference: str = "gpt-4"
    settings: ConversationSettings = field(default_factory=ConversationSettings)
    messages: List[Message] = field(default_factory=list)
    statistics: ConversationStatistics = field(default_factory=ConversationStatistics)
    sharing: SharingInfo = field(default_factory=SharingInfo)
    created_at: datetime = field(default_factory=datetime.utcnow)
    updated_at: datetime = field(default_factory=datetime.utcnow)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for MongoDB storage."""
        doc = {
            "user_id": self.user_id,
            "title": self.title,
            "status": self.status.value,
            "tags": self.tags,
            "category": self.category.value,
            "model_preference": self.model_preference,
            "settings": self.settings.to_dict(),
            "messages": [msg.to_dict() for msg in self.messages],
            "statistics": self.statistics.to_dict(),
            "sharing": self.sharing.to_dict(),
            "created_at": self.created_at,
            "updated_at": self.updated_at
        }
        
        if self._id:
            doc["_id"] = self._id
            
        return doc
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'Conversation':
        """Create from MongoDB document."""
        data = data.copy()
        
        # Convert string enums back to enum types
        data['status'] = ConversationStatus(data.get('status', 'active'))
        data['category'] = ConversationCategory(data.get('category', '其他'))
        
        # Convert nested structures
        data['settings'] = ConversationSettings.from_dict(data.get('settings', {}))
        data['messages'] = [Message.from_dict(msg) for msg in data.get('messages', [])]
        data['statistics'] = ConversationStatistics.from_dict(data.get('statistics', {}))
        data['sharing'] = SharingInfo.from_dict(data.get('sharing', {}))
        
        return cls(**data)
    
    def add_message(self, message: Message):
        """Add a message to the conversation."""
        self.messages.append(message)
        self.statistics.update_with_message(message)
        self.updated_at = datetime.utcnow()
    
    def get_message_by_id(self, message_id: str) -> Optional[Message]:
        """Get a message by its ID."""
        for msg in self.messages:
            if msg.id == message_id:
                return msg
        return None
    
    def update_message(self, message_id: str, new_content: str):
        """Update a message content and track edit history."""
        msg = self.get_message_by_id(message_id)
        if msg:
            # Save to edit history
            msg.metadata.edit_history.append({
                "content": msg.content,
                "edited_at": datetime.utcnow()
            })
            msg.content = new_content
            msg.metadata.edited = True
            self.updated_at = datetime.utcnow()
    
    def soft_delete(self):
        """Soft delete the conversation."""
        self.status = ConversationStatus.DELETED
        self.updated_at = datetime.utcnow()
    
    def archive(self):
        """Archive the conversation."""
        self.status = ConversationStatus.ARCHIVED
        self.updated_at = datetime.utcnow()
    
    def generate_share_link(self, expires_at: Optional[datetime] = None, password: Optional[str] = None):
        """Generate a sharing link for the conversation."""
        self.sharing.is_shared = True
        self.sharing.share_id = str(uuid.uuid4())
        self.sharing.share_expires_at = expires_at
        self.sharing.share_password = password
        self.updated_at = datetime.utcnow()
        return self.sharing.share_id
    
    def revoke_share_link(self):
        """Revoke the sharing link."""
        self.sharing.is_shared = False
        self.sharing.share_id = None
        self.sharing.share_expires_at = None
        self.sharing.share_password = None
        self.sharing.share_views = 0
        self.updated_at = datetime.utcnow()
    
    def increment_share_views(self):
        """Increment the share view counter."""
        self.sharing.share_views += 1


# TypeScript Interface Definitions (for documentation)
TYPESCRIPT_INTERFACES = """
interface Conversation {
  id: string;
  userId: string;
  title: string;
  status: 'active' | 'archived' | 'deleted';
  tags: string[];
  category: string;
  modelPreference: string;
  settings: ConversationSettings;
  messages: Message[];
  statistics: ConversationStats;
  sharing: SharingInfo;
  createdAt: Date;
  updatedAt: Date;
}

interface Message {
  id: string;
  role: 'user' | 'assistant' | 'system';
  content: string;
  timestamp: Date;
  model: string;
  tokensUsed: number;
  cost: number;
  metadata: MessageMetadata;
}

interface ConversationSettings {
  temperature: number;
  maxTokens: number;
  topP: number;
  frequencyPenalty: number;
  presencePenalty: number;
}

interface ConversationStats {
  totalMessages: number;
  totalTokens: number;
  totalCost: number;
  lastMessageAt: Date | null;
}

interface SharingInfo {
  isShared: boolean;
  shareId: string | null;
  shareExpiresAt: Date | null;
  shareViews: number;
  sharePassword: string | null;
}

interface MessageMetadata {
  edited: boolean;
  editHistory: Array<{
    content: string;
    editedAt: Date;
  }>;
  attachments: Array<any>;
}
"""